package demo.dcs.aspect;

import demo.dcs.MertricListener;
import demo.dcs.MertricSendSchedule;
import demo.dcs.util.LinuxShellUtil;
import demo.dcs.vo.XdcsSparkConstant;
import demo.java.lang.management.jvm.AppTraceConfig;
import demo.java.lang.management.vo.MetricSpan;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.management.MBeanAttributeInfo;
import javax.management.MBeanInfo;
import javax.management.MBeanServer;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.Query;
import javax.management.openmbean.CompositeDataSupport;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryUsage;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
 * @author mandy.hu
 * jmx实现
 */
public class TomcatPoolAspectJ extends AbstractXdcsAspectJ implements MertricListener {
	
	private static final Logger logger = LoggerFactory.getLogger(TomcatPoolAspectJ.class);
	
	private static MBeanServer mbsc = ManagementFactory.getPlatformMBeanServer();

    private static int tomcatPort;

    static {
        try {
            Set<ObjectName> objs = mbsc.queryNames(new ObjectName("*:type=Connector,*"),
                    Query.match(Query.attr("protocol"), Query.value("HTTP/1.1")));
            for (Iterator<ObjectName> i = objs.iterator(); i.hasNext();) {
                ObjectName obj = i.next();
                String port = obj.getKeyProperty("port");
                if (StringUtils.isNumeric(port)){
                    tomcatPort = Integer.parseInt(port);
                }
            }
        }catch (Throwable e){
            logger.error("TomcatPoolAspectJ get port error {}", e.getMessage());
        }
    }

    public TomcatPoolAspectJ(){
	    MertricSendSchedule.getMertricSendSchedule().addMertricListener(this);
	}

    public MetricSpan createMetricSpan(long timestamp) {
        return createMetricSpan(timestamp, null);
    }

	public MetricSpan createMetricSpan(long timestamp, Map<String, String> tags) {
		MetricSpan metricSpan = new MetricSpan();
    	metricSpan.setTimestamp(timestamp);
    	metricSpan.setMetric(XdcsSparkConstant.TOMCAT_METRIC);
    	metricSpan.getTags().put(XdcsSparkConstant.TAG_APP, AppTraceConfig.getLocalConfig().getAppName());
    	metricSpan.getTags().put(XdcsSparkConstant.TAG_IP, AppTraceConfig.getLocalConfig().getIp());
        if (tags!=null){
            metricSpan.getTags().putAll(tags);
        }
		return metricSpan;
	}
	 
	@Override
	public List<MetricSpan> collectMetric() {
		List<MetricSpan> metricSpans = new ArrayList<MetricSpan>();
		long timestamp =  System.currentTimeMillis();
		try { 
	           /*ObjectName managerObjName = new ObjectName("Catalina:type=Manager,*");
	           Set<ObjectName> s=mbsc.queryNames(managerObjName, null);  
	           for (ObjectName obj:s){    
	               ObjectName objname=new ObjectName(obj.getCanonicalName());  
	               MetricSpan metricSpan = createMetricSpan(timestamp);
	               metricSpan.getTags().put(XdcsSparkConstant.TOMCAT_METRIC_KEY,XdcsSparkConstant.TOMCAT_MAX_SESSIONS);
	               metricSpan.setValue( mbsc.getAttribute( objname, "maxActiveSessions"));
	               
	               MetricSpan metricSpan1 = createMetricSpan(timestamp);
	               metricSpan1.getTags().put(XdcsSparkConstant.TOMCAT_METRIC_KEY,XdcsSparkConstant.TOMCAT_ACTIVE_SESSIONS);
	               metricSpan1.setValue( mbsc.getAttribute( objname, "activeSessions"));
	               
	               MetricSpan metricSpan2 = createMetricSpan(timestamp);
	               metricSpan2.getTags().put(XdcsSparkConstant.TOMCAT_METRIC_KEY,XdcsSparkConstant.TOMCAT_SESSION_COUNTER);
	               metricSpan2.setValue( mbsc.getAttribute( objname, "sessionCounter"));
	               metricSpans.add(metricSpan);
	               metricSpans.add(metricSpan1);
	               metricSpans.add(metricSpan2);
	           }*/
	           ObjectName threadpoolObjName = new ObjectName("Catalina:name=tomcatThreadPool,type=Executor");
	           Set<ObjectName> s2=mbsc.queryNames(threadpoolObjName, null);
	           for (ObjectName obj:s2){  
	               ObjectName objname=new ObjectName(obj.getCanonicalName());
                   Map<String, String> globalTag = new HashMap<String, String>();
                   String poolName = obj.getKeyProperty("name");
                   globalTag.put(XdcsSparkConstant.TOMCAT_THREAD_POOL_NAME, poolName);
	               MetricSpan metricSpan = createMetricSpan(timestamp, globalTag);
	               metricSpan.getTags().put(XdcsSparkConstant.TOMCAT_METRIC_KEY,XdcsSparkConstant.TOMCAT_MAX_THREADS);
	               metricSpan.setValue( mbsc.getAttribute( objname, "maxThreads"));
	               
	               MetricSpan metricSpan1 = createMetricSpan(timestamp, globalTag);
	               metricSpan1.getTags().put(XdcsSparkConstant.TOMCAT_METRIC_KEY,XdcsSparkConstant.TOMCAT_CURRENT_QUEUE_SIZE);
	               metricSpan1.setValue( mbsc.getAttribute( objname, "queueSize"));
	               
	               MetricSpan metricSpan2 = createMetricSpan(timestamp, globalTag);
	               metricSpan2.getTags().put(XdcsSparkConstant.TOMCAT_METRIC_KEY,XdcsSparkConstant.TOMCAT_CURRENT_POOL_SIZE);
	               metricSpan2.setValue( mbsc.getAttribute( objname, "poolSize"));

                   MetricSpan metricSpan3 = createMetricSpan(timestamp, globalTag);
                   metricSpan3.getTags().put(XdcsSparkConstant.TOMCAT_METRIC_KEY,XdcsSparkConstant.TOMCAT_CURRENT_ACTIVE_COUNT);
                   metricSpan3.setValue( mbsc.getAttribute( objname, "activeCount"));

	               metricSpans.add(metricSpan);
	               metricSpans.add(metricSpan1);
	               metricSpans.add(metricSpan2);
	               metricSpans.add(metricSpan3);
	           }
               /*if (tomcatPort!=0){
                   try {
                       //tomcat各个状态的连接数
                       metricSpans.add(tomcatConnectionMetric("ESTABLISHED", timestamp));
                       metricSpans.add(tomcatConnectionMetric("TIME_WAIT", timestamp));
                       metricSpans.add(tomcatConnectionMetric(null, timestamp));
                   }catch (Exception e){
                       logger.error("TomcatPoolAspectJ tomcatConnectionMetric error", e);
                   }
               }*/
		} catch (Throwable e) {
			logger.error("TomcatPoolAspectJ getTomcatMetric error", e);
		}
		return metricSpans;
	}

    private MetricSpan tomcatConnectionMetric(String tcpStatus, long timestamp){
        Map<String, String> globalTag = new HashMap<String, String>();
        MetricSpan metricSpan = createMetricSpan(timestamp, globalTag);
        String ip = AppTraceConfig.getLocalConfig().getIp()+":"+tomcatPort;
        if (StringUtils.isNotBlank(tcpStatus)){
            metricSpan.getTags().put(XdcsSparkConstant.TOMCAT_METRIC_KEY, tcpStatus);
            String count = LinuxShellUtil.exec(String.format("netstat -na|grep %s|grep %s|wc -l", tcpStatus, ip));
            if (count!=null){
                metricSpan.setValue(Integer.parseInt(count));
            }else {
                metricSpan.setValue(-2);
            }
        }else {
            metricSpan.getTags().put(XdcsSparkConstant.TOMCAT_METRIC_KEY, "ALL_STATUS");
            String count = LinuxShellUtil.exec(String.format("netstat -na|grep %s|wc -l", ip));
            if (count!=null){
                metricSpan.setValue(Integer.parseInt(count));
            }else {
                metricSpan.setValue(-2);
            }
        }

        return metricSpan;
    }

    @Override
    public boolean isSame(MertricListener listener) {
        if (listener==null)
            return false;
        if (!(listener instanceof TomcatPoolAspectJ))
            return false;
        if (((TomcatPoolAspectJ) listener).mbsc!=this.mbsc)
            return false;
        return true;
    }

    @Override
	public void beforeAspectJ(Object signature, Object instance, Object[] args) {

		
	}

	@Override
	public void afterAspectJ(Object result, Object signature, Object instance, Object[] args, Throwable throwable) {
		
	} 
    public  void init() {  
        try {  
             
           //端口最好是动态取得  
           ObjectName threadObjName = new ObjectName("Catalina:type=ThreadPool,name=http-8080");  
           MBeanInfo mbInfo = mbsc.getMBeanInfo(threadObjName);  
             
           String attrName = "currentThreadCount";//tomcat的线程数对应的属性值  
           MBeanAttributeInfo[] mbAttributes = mbInfo.getAttributes();  
           logger.info("currentThreadCount:"+mbsc.getAttribute(threadObjName, attrName));
             
           //heap  
           for(int j=0;j <mbsc.getDomains().length;j++){   
               logger.info("###########"+mbsc.getDomains()[j]);
           }   
           Set MBeanset = mbsc.queryMBeans(null, null);  
           logger.info("MBeanset.size() : " + MBeanset.size());
           Iterator MBeansetIterator = MBeanset.iterator();  
           while (MBeansetIterator.hasNext()) {   
               ObjectInstance objectInstance = (ObjectInstance)MBeansetIterator.next();  
               ObjectName objectName = objectInstance.getObjectName();  
               String canonicalName = objectName.getCanonicalName();  
               logger.info("canonicalName : " + canonicalName);
               if (canonicalName.equals("Catalina:host=localhost,type=Cluster"))      {    
                   // Get details of cluster MBeans  
                   logger.info("Cluster MBeans Details:");
                   logger.info("=========================================");
                   //getMBeansDetails(canonicalName);  
                  String canonicalKeyPropList = objectName.getCanonicalKeyPropertyListString();  
              }  
           }  
           //------------------------- system ----------------------  
           ObjectName runtimeObjName = new ObjectName("java.lang:type=Runtime");  
           logger.info("厂商:"+ (String)mbsc.getAttribute(runtimeObjName, "VmVendor"));
           logger.info("程序:"+ (String)mbsc.getAttribute(runtimeObjName, "VmName"));
           logger.info("版本:"+ (String)mbsc.getAttribute(runtimeObjName, "VmVersion"));
           Date starttime=new Date((Long)mbsc.getAttribute(runtimeObjName, "StartTime"));  
           SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
           logger.info("启动时间:"+df.format(starttime));
             
           Long timespan=(Long)mbsc.getAttribute(runtimeObjName, "Uptime");  
           logger.info("连续工作时间:"+formatTimeSpan(timespan));
           //------------------------ JVM -------------------------  
           //堆使用率  
           ObjectName heapObjName = new ObjectName("java.lang:type=Memory");  
           MemoryUsage heapMemoryUsage =  MemoryUsage.from((CompositeDataSupport)mbsc.getAttribute(heapObjName, "HeapMemoryUsage"));  
           long maxMemory = heapMemoryUsage.getMax();//堆最大  
           long commitMemory = heapMemoryUsage.getCommitted();//堆当前分配  
           long usedMemory = heapMemoryUsage.getUsed();  
           logger.info("heap:"+(double)usedMemory*100/commitMemory+"%");//堆使用率
             
           MemoryUsage nonheapMemoryUsage =  MemoryUsage.from((CompositeDataSupport)mbsc.getAttribute(heapObjName, "NonHeapMemoryUsage"));             
           long noncommitMemory = nonheapMemoryUsage.getCommitted();  
           long nonusedMemory = heapMemoryUsage.getUsed();  
           logger.info("nonheap:"+(double)nonusedMemory*100/noncommitMemory+"%");
             
           ObjectName permObjName = new ObjectName("java.lang:type=MemoryPool,name=Perm Gen");  
           MemoryUsage permGenUsage =  MemoryUsage.from((CompositeDataSupport)mbsc.getAttribute(permObjName, "Usage"));             
           long committed = permGenUsage.getCommitted();//持久堆大小  
           long used = heapMemoryUsage.getUsed();//  
           logger.info("perm gen:"+(double)used*100/committed+"%");//持久堆使用率
             
           //-------------------- Session ---------------   
           ObjectName managerObjName = new ObjectName("Catalina:type=Manager,*");  
           Set<ObjectName> s=mbsc.queryNames(managerObjName, null);  
           for (ObjectName obj:s){  
               logger.info("应用名:"+obj.getKeyProperty("path"));
               ObjectName objname=new ObjectName(obj.getCanonicalName());  
               logger.info("最大会话数:"+ mbsc.getAttribute( objname, "maxActiveSessions"));
               logger.info("会话数:"+ mbsc.getAttribute( objname, "activeSessions"));
               logger.info("活动会话数:"+ mbsc.getAttribute( objname, "sessionCounter"));
           }  
             
           //----------------- Thread Pool ----------------  
           ObjectName threadpoolObjName = new ObjectName("Catalina:type=ThreadPool,*");  
           Set<ObjectName> s2=mbsc.queryNames(threadpoolObjName, null);  
           for (ObjectName obj:s2){  
               logger.info("端口名:"+obj.getKeyProperty("name"));
               ObjectName objname=new ObjectName(obj.getCanonicalName());  
               logger.info("最大线程数:"+ mbsc.getAttribute( objname, "maxThreads"));
               logger.info("当前线程数:"+ mbsc.getAttribute( objname, "currentThreadCount"));
               logger.info("繁忙线程数:"+ mbsc.getAttribute( objname, "currentThreadsBusy"));
           }  
                 
        } catch (Exception e) {  
           logger.error(e.getMessage());
        }  
    }

    public static int getTomcatPort() {
        return tomcatPort;
    }

    public static String formatTimeSpan(long span){
        long minseconds = span % 1000;  
          
        span = span /1000;  
        long seconds = span % 60;  
          
        span = span / 60;  
        long mins = span % 60;  
          
        span = span / 60;  
        long hours = span % 24;  
  
        span = span / 24;  
        long days = span;
        Formatter formatter = new Formatter();
        String timeSpan = formatter.format("%1$d天 %2$02d:%3$02d:%4$02d.%5$03d", days,hours,mins,seconds,minseconds).toString();
        formatter.close();

        return timeSpan;
    }

}
